#include "RCGame.h"

int RCGame::int_AppRunning;
HWND RCGame::hWnd;

RCGame::RCGame(void)
{ 
	str_Title = L"Randomchaos DX11 Library ver 1.0";
	int_XPos = 0;
	int_YPos = 0;
	int_Width = 800;
	int_Height = 480;

	clearColor = XMFLOAT4( 0.392156862745098f, 0.5843137254901961f, 0.9294117647058824f, 1.0f);
}


RCGame::~RCGame(void)
{ 
	vGameComponents.clear();
	vUpdateables.clear();
	vDrawables.clear();
}


void RCGame::Draw(float time,std::vector<IDrawable*>* drawables)
{	
	for(auto c = drawables->begin();c != drawables->end();c++)
	{
		if((*c)->getVisible())
			(*c)->Draw(0);
	}	
}

void RCGame::setClearColor(XMFLOAT4 color)
{
	clearColor = color;
}

void RCGame::Initialize(LPCTSTR str_Title,int int_XPos, int int_YPos, int int_Width, int int_Height, WNDPROC WinProc)
{
	GraphicsDevice.InitializeWindow((void**)this, str_Title,int_XPos, int_YPos, int_Width, int_Height, WinProc, COLOR_DESKTOP);	
	GraphicsDevice.Initialize();

	KeyboardManager.InitializeDevice(GraphicsDevice.getWindowHandle());

}

void RCGame::Initialize()
{
	Initialize(str_Title,int_XPos,int_YPos,int_Width,int_Height, winProc);

	// init all the componenets
	for(auto c = vGameComponents.begin();c != vGameComponents.end();c++)
	{
		(*c)->Initialize();
	}
}

void RCGame::Update(float time, std::vector<IUpdateable*>* updateambles)
{
	if(updateambles != 0)
	{
		// Componente update
		for(auto c = updateambles->begin();c != updateambles->end();c++)
		{
			if((*c)->getEnabled())
				(*c)->Update(time);
		}

	}
	if(KeyboardManager.getEnabled())
		KeyboardManager.Update(time);
}


HRESULT RCGame::CompileShaderFromFile( WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut )
{
    HRESULT hr = S_OK;

    DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
    // Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
    // Setting this flag improves the shader debugging experience, but still allows 
    // the shaders to be optimized and to run exactly the way they will run in 
    // the release configuration of this program.
    dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif

    ID3DBlob* pErrorBlob;
    hr = D3DX11CompileFromFile( szFileName, NULL, NULL, szEntryPoint, szShaderModel, 
        dwShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL );
    if( FAILED(hr) )
    {
        if( pErrorBlob != NULL )
            OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() );
        if( pErrorBlob ) pErrorBlob->Release();
        return hr;
    }
    if( pErrorBlob ) pErrorBlob->Release();

    return S_OK;
}

XMVECTOR RCGame::GetTextureSize(_In_ ID3D11ShaderResourceView* texture)
{
    // Convert resource view to underlying resource.
    ComPtr<ID3D11Resource> resource;

    texture->GetResource(&resource);
    
    // Cast to texture.
    ComPtr<ID3D11Texture2D> texture2D;
    
    if (FAILED(resource.As(&texture2D)))
    {
        throw std::exception("SpriteBatch can only draw Texture2D resources");
    }

    // Query the texture size.
    D3D11_TEXTURE2D_DESC desc;

    texture2D->GetDesc(&desc);

    // Convert to vector format.
    XMVECTOR size = XMVectorMergeXY(XMLoadInt(&desc.Width),
                                    XMLoadInt(&desc.Height));

    return XMConvertVectorUIntToFloat(size, 0);
}

void RCGame::setTitle(LPCTSTR title)
{
	str_Title = title;
}
void RCGame::setWindowPos(int x, int y)
{
	int_XPos = x;
	int_YPos = y;
}
void RCGame::setWindowDimension(int width, int height)
{
	int_Width = width;
	int_Height = height;
}

LRESULT CALLBACK RCGame::WinProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	
    switch(message)
	{
		case WM_CLOSE:
		{
			RCGame::Stop();
			break;
		}

		case WM_SIZE:
		{
			RECT rc;
			GetClientRect( hWnd , &rc );
			UINT width = rc.right - rc.left;
			UINT height = rc.bottom - rc.top;

			
			break;
		}
	}	

	return DefWindowProc(hWnd,message,wParam,lParam);
}

LRESULT CALLBACK RCGame::winProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{	
   RCGame* pParent;

   // Get pointer to window
   if(message == WM_CREATE)
   {
      pParent = (RCGame*)((LPCREATESTRUCT)lParam)->lpCreateParams;
      SetWindowLongPtr(hWnd,GWL_USERDATA,(LONG_PTR)pParent);
   }
   else
   {
      pParent = (RCGame*)GetWindowLongPtr(hWnd,GWL_USERDATA);
      if(!pParent) 
		  return DefWindowProc(hWnd,message,wParam,lParam);
   }
      
   return pParent->WinProc(hWnd, message,wParam,lParam);
}
void RCGame::Stop()
{
	int_AppRunning = 0;
}
void RCGame::Run(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdshow)
{
	int_AppRunning = 1;

	MSG msg_Message;	

	Initialize();

	hWnd = GraphicsDevice.getWindowHandle();

	while(int_AppRunning)
	{
		if(PeekMessage(&msg_Message,hWnd,0,0,PM_REMOVE))
				DispatchMessage(&msg_Message);

		Update(0,&vUpdateables);
		
		Draw(0,&vDrawables);

		GraphicsDevice.Present( 1, 0 );
	}

	DestroyWindow(hWnd);

	vUpdateables.clear();
	vDrawables.clear();
	vGameComponents.clear();
}
IRCGraphicsDevice* RCGame::getGraphicsDevice()
{
	return &GraphicsDevice;
}

void RCGame::AddComponent(std::shared_ptr<IRCGameComponent> component)
{
	component->setGame(this);
		
	IUpdateable*  objUpd = dynamic_cast<IUpdateable*>(component.get());

	if(objUpd != nullptr)
		vUpdateables.push_back(objUpd);

	IDrawable* objDrw = dynamic_cast<IDrawable*>(component.get());

	if(objDrw != nullptr)
		vDrawables.push_back(objDrw);
		
	vGameComponents.push_back(component);
}